#pragma once

#include <memory>
#include <vector>
#include <string>
#include <locale>
//#include <codecvt>

#define LOG_LEVEL_TRACE 0
#define LOG_LEVEL_DEBUG 1
#define LOG_LEVEL_INFO 2
#define LOG_LEVEL_WARN 3
#define LOG_LEVEL_ERROR 4
#define LOG_LEVEL_CRITICAL 5
#define LOG_LEVEL_OFF 6

//#define LTrace(msg,...)     LogManager::Instance().Trace(msg,__VA_ARGS__)
//#define LDebug(msg,...)     LogManager::Instance().Debug(msg,__VA_ARGS__)
//#define LInfo(msg,...)      LogManager::Instance().Info(msg,__VA_ARGS__)
//#define LWarn(msg,...)      LogManager::Instance().Warn(msg,__VA_ARGS__)
//#define LError(msg,...)     LogManager::Instance().Error(msg,__VA_ARGS__)
//#define LCritical(msg,...)  LogManager::Instance().Critical(msg,__VA_ARGS__)

class  LogManager
{
public:
	static LogManager& Instance();

public:
	void SetLevel(int level);

public:
	void Trace(const std::wstring& msg);
	void Debug(const std::wstring& msg);
	void Info(const std::wstring& msg);
	void Warn(const std::wstring& msg);
	void Error(const std::wstring& msg);
	void Critical(const std::wstring& msg);

	void Trace(const std::string& msg);
	void Debug(const std::string& msg);
	void Info(const std::string& msg);
	void Warn(const std::string& msg);
	void Error(const std::string& msg);
	void Critical(const std::string& msg);

public:

	template<typename ... Args>
	std::string Formate(const std::string& format, Args ... args)
	{
		auto size = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0'
		std::unique_ptr<char[]> buf(new char[size]);
		std::snprintf(buf.get(), size, format.c_str(), args ...);

		std::string result = std::string(buf.get(), buf.get() + size - 1);

		return result;
	}

	template<typename ... Args>
	void Trace(const std::string& format, Args ... args)
	{
		Trace(Formate(format, std::forward<Args>(args)...));
	}

	template<typename ... Args>
	void Debug(const std::string& format, Args ... args)
	{
		Debug(Formate(format, std::forward<Args>(args)...));
	}

	template<typename ... Args>
	void Info(const std::string& format, Args ... args)
	{
		Info(Formate(format, std::forward<Args>(args)...));
	}

	template<typename ... Args>
	void Warn(const std::string& format, Args ... args)
	{
		Warn(Formate(format, std::forward<Args>(args)...));
	}

	template<typename ... Args>
	void Error(const std::string& format, Args ... args)
	{
		Error(Formate(format, std::forward<Args>(args)...));
	}

	template<typename ... Args>
	void Critical(const std::string& format, Args ... args)
	{
		Critical(Formate(format, std::forward<Args>(args)...));
	}

private:
	class Impl;
	std::shared_ptr<Impl> m_impl;

private:
	LogManager();
	~LogManager();
	LogManager(const LogManager &other) = delete;
	LogManager& operator=(const LogManager &other) = delete;
};

